"""
This module takes care of parsing arguments from client and determing what
kind of call has been requested.
"""
import re

from pythontojavascript import ObjectSerializer

OBJNAME = "_PTJS_ObjectName"
GETMETHOD = "_PTJS_GetMethod"
GETARGS = "_PTJS_GetArgs"
METHOD = "_PTJS_Method"
METHODARGS = "_PTJS_MethodArgs"
MODULE = "_PTJS_Module"

class UnauthorizedExecuteRequest(Exception): pass
class UnknownExecuteRequest(Exception): pass
class UnknownExecuteError(Exception): pass

def execute(**args):
  """
  This script figures out what we're trying to run.
  """
  objname = args.get(OBJNAME)
  if objname: del(args[OBJNAME])
  getmethod = args.get(GETMETHOD)
  if getmethod: del(args[GETMETHOD])
  getargs = args.get(GETARGS)
  if getargs: del(args[GETARGS])
  method = args.get(METHOD)
  if method: del(args[METHOD])
  methodargs = args.get(METHODARGS)
  if methodargs: del(args[METHODARGS])
  module = args.get(MODULE)
  if module: del(args[MODULE])

  authenticateObjectAndMethods(objname,getmethod,method)
  authenticateModule(module)

  # If we have no methods defined, we want to create the object.
  if objname and not getmethod and not method:
    return createObject(objname,methodargs)
  if objname and getmethod and method:
    return runInstanceMethod(objname,getmethod,getargs,method,methodargs)
  if objname and method:
    return runClassMethod(objname,method,methodargs)
  if module and method:
    return runMethodFromModule(module,method,methodargs)
  raise UnknownExecuteRequest("Sorry, I don't know what you want with args:%s"
                               % args)
  
def multiExecute(**args):
  """
  This puts a bunch of AJAX calls together into one, to speed things up.
  """
  pass
  
def authenticateObjectAndMethods(objname,getmethod,method):
  """
  We check to see if these objects/methods are allowed to be called.
  Otherwise, raise a UnauthorizedExecuteRequest error.
  """
  if objname:
    obj = ObjectSerializer.getObjectByName(objname)
    if not obj:
      raise UnauthorizedExecuteRequest("You requested the object '%s', but"
                                       " it has not been compiled." % objname)
    auth_methods = ObjectSerializer.getAuthorizedMethods(obj)
    if not method in auth_methods:
      raise UnauthorizedExecuteRequest(
       "You requested method '%s' on object '%s', but it has not been compiled."
       "\nAuthorized methods:%s" % (method,objname,"\n".join(auth_methods)))
    auth_methods = ObjectSerializer.getAuthorizedMethods()
  if getmethod and not getmethod in auth_methods:
    raise UnauthorizedExecuteRequest("You requested getmethod '%s', but it has "
      "not been compiled.\nAuthorized getmethods: %s" 
      % (getmethod,"\n".join(auth_methods)))

def authenticateModule(module):
  if not module: return
  auth_modules = ObjectSerializer.getAuthorizedModules()
  # We allow any module that is a child module of something in "auth_modules"
  auth = False
  for auth_mod in auth_modules:
    if re.match(auth_mod,module):
      auth = True
  if not auth:
    raise UnauthorizedExecuteRequest("You requested module '%s', but it has not"
                                     " been authorized.\nAuthorized modules: %s"
                                     % (module,"\n".join(auth_modules)))
  
def createObject(objname,kwargs):
  cls = ObjectSerializer.getObjectByName(objname)
  return cls(**kwargs)

def runInstanceMethod(objname,getmethod,getargs,method,methodargs):
  cls = ObjectSerializer.getObjectByName(objname)
  obj = getattr(cls,getmethod)(**getargs)
  # Python supports a "property" object. Javascript does not.
  # That means that our "method" (from JS) might actually be a "property" in
  # Python. Let's check.
  if type(getattr(cls,method)) == property:
    if methodargs:
      # DO WE REALLY WANT TO PASS IN A dictionary?
      return setattr(obj,method,methodargs) 
    else:
      return getattr(obj,method)
  else:
    # WE NEED TO DOCUMENT THIS BETTER - the fact that we either take kwargs, or
    # just one arg...
    if methodargs == None:
      return getattr(obj,method)()
    if type(methodargs) == dict:
      return getattr(obj,method)(**methodargs)
    else:
      return getattr(obj,method)(methodargs)
  raise UnknownExecuteError("runInstanceMethod couldn't figure out what to run")      

def runClassMethod(objname,method,methodargs):
  cls = ObjectSerializer.getObjectByName(objname)
  if methodargs == None:
    return getattr(cls,method)()
  if type(methodargs) == dict:
    return getattr(cls,method)(**methodargs)
  else:
    return getattr(cls,method)(methodargs)
  raise UnknownExecuteError("runClassMethod couldn't figure out what to run")      
  
def runMethodFromModule(module,method,methodargs):
  try:
    mod = __import__(module,globals(),locals(),[module])
  except ImportError:
    import sys
    import os
    raise ImportError("Module " + module + " not found. Path: " + str(sys.path) + ". CWD: " + os.getcwd())
  return getattr(mod,method)(**methodargs)
